/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package GA;


import java.util.Random;


/**
 *
 * @author ollo
 */
public class BinaryGenome {
    
    private static byte[] locusMasks = new byte[] { -1, 127, 63, 31, 15, 7, 3, 1};
    public static final int MINIMIZE = -1, MAXIMIZE = 1;
    private static Random rand = new Random(System.currentTimeMillis());
    private byte[] genome = null;
    private double fitness = Double.NaN;
    //private double fitness = 0;
    private int overflow, nLocus, length, type;
    private boolean fitnessSet = false;
    
    public int getNLocus() { return nLocus; }
    public int getOverflow() { return overflow; }
    public int getType() { return type; }
    public void setFitness(double val) { fitness = val; fitnessSet = true; }
    public double getFitness() { return fitness; }
    public boolean isFitnessSet() { return fitnessSet; }
    public byte[] getGenome() { return genome; }

    public BinaryGenome(int length, int type)
    {
        this.length = length;
        this.type = type;
        overflow = length % 8;

        nLocus = (overflow == 0) ? length / 8 : 1 + length / 8;

        genome = new byte[nLocus];
        rand.nextBytes(genome);
    }

    public BinaryGenome(BinaryGenome predecessor)
    {
        nLocus = predecessor.nLocus;
        overflow = predecessor.overflow;
        length = predecessor.length;
        type = predecessor.type;

        genome = new byte[nLocus];
        System.arraycopy(predecessor.genome, 0, genome, 0, nLocus);
    }

    public BinaryGenome(BinaryGenome pre1, BinaryGenome pre2, int xPoint)
    {
        nLocus = pre1.nLocus;
        overflow = pre1.overflow;
        length = pre1.length;
        type = pre1.type;
        
        genome = new byte[nLocus];

        int xLocus = xPoint / 8;
        int xInLocus = xPoint % 8;
        System.arraycopy(pre1.genome, 0, genome, 0, xLocus);

        genome[xLocus] =(byte) (pre1.genome[xLocus] & ~locusMasks[xInLocus]
                |  pre2.genome[xLocus] & locusMasks[xInLocus]);

        if (xLocus < nLocus)
            System.arraycopy(pre2.genome, xLocus+1, genome, xLocus+1, nLocus-1-xLocus);
    }


    @Override
    public String toString()
    {
        String line = "";
        
        for (int i = 0; i < nLocus; i++)
            line += byteToString(genome[i]);
        
        return line.substring(0, length+nLocus-1);
    }
    
    public static String byteToString(byte b) 
    {
        StringBuilder bin = new StringBuilder("00000000 ");
        for (int i = 0; i < 8; i++) {
            if (((1 << i) & b) != 0)
                bin.setCharAt(7-i, '1');
        }
        return bin.toString();
    }

    public static BinaryGenome[] onePointX(BinaryGenome mom, BinaryGenome dad)
    {
        int xPoint = rand.nextInt(mom.length);
        
        BinaryGenome[] children = new BinaryGenome[2];

        children[0] = new BinaryGenome(mom, dad, xPoint);
        children[1] = new BinaryGenome(dad, mom, xPoint);

        return children;
    }

    public static BinaryGenome mutateLocus(BinaryGenome gene)
    {
        BinaryGenome mutation = new BinaryGenome(gene);

        int mutationLocus = rand.nextInt(mutation.nLocus);
        byte[] muta = new byte[1];
        rand.nextBytes(muta);
        mutation.genome[mutationLocus] = muta[0];
        return mutation;
    }
    
    public boolean isFitter(BinaryGenome gene) 
    {
        if (!fitnessSet)
            return false;
        if (type == MAXIMIZE) {
            if (gene.getFitness() < fitness)
                return true;
        } else 
            if (gene.getFitness() > fitness)
                return true;
        return false;
    }
    
    /*@Override ****implements Comparable
    public int compareTo(Object o) 
    {
        if(o instanceof Double)
        if(fitness != Double.NaN) 
        {
            
            if(o.fitness != Double.NaN) 
            {
                if (type == MAXIMIZE)
                    if (o.fitness == fitness)
                        return 0;
                    else if (o.fitness < fitness)
                        return -1;
                    else
                        return 1;   
                else if (type == MINIMIZE)
                    if (o.fitness == fitness)
                        return 0;
                    else if (o.fitness > fitness)
                        return -1;
                    else
                        return 1; 
            }
        }
        return 0;
    }*/
}
